﻿namespace Neovolve.Toolkit.IntegrationTests.Communication
{
    using System;
    using System.Configuration;
    using System.ServiceModel;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Neovolve.Toolkit.Communication;
    using Neovolve.Toolkit.IntegrationTests.Communication.Security;

    /// <summary>
    /// The <see cref="ChannelProxyHandlerTests"/>
    ///   class is used to test the <see cref="ChannelProxyHandler{T}"/> class.
    /// </summary>
    [TestClass]
    public class ChannelProxyHandlerTests
    {
        /// <summary>
        /// Runs test for channel proxy handler can call A service after first call fails.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerCanCallAServiceAfterFirstCallFailsTest()
        {
            const String ServiceAddress = "net.pipe://localhost/ChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(TestService)))
            {
                host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), ServiceAddress);

                using (ChannelProxyHandler<ITestService> handler = new ChannelProxyHandler<ITestService>())
                {
                    ITestService channel = (ITestService)handler.GetTransparentProxy();

                    try
                    {
                        channel.DoSomething();
                    }
                    catch (CommunicationException)
                    {
                        // Ignore this exception
                    }

                    host.Open();

                    channel.DoSomething();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler can call service when previous call faulted the channel.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerCanCallServiceWhenPreviousCallFaultedTheChannelTest()
        {
            const String ServiceAddress = "net.pipe://localhost/ChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(TestService)))
            {
                host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<ITestService> handler = new ChannelProxyHandler<ITestService>())
                {
                    ITestService channel = (ITestService)handler.GetTransparentProxy();

                    try
                    {
                        channel.ThrowException();
                    }
                    catch (CommunicationException)
                    {
                        // Ignore this exception
                    }

                    channel.DoSomething();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler can destroy faulted channel.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerCanDestroyFaultedChannelTest()
        {
            const String ServiceAddress = "net.pipe://localhost/ChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(TestService)))
            {
                host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<ITestService> handler = new ChannelProxyHandler<ITestService>())
                {
                    ITestService channel = (ITestService)handler.GetTransparentProxy();

                    try
                    {
                        channel.ThrowException();
                    }
                    catch (CommunicationException)
                    {
                        // Ignore this exception
                    }
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler creates endpoint from configuration name.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerCreatesEndpointFromConfigurationNameTest()
        {
            const String ServiceAddress = "net.pipe://localhost/NamedChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(PasswordService)))
            {
                host.AddServiceEndpoint(typeof(IPasswordService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<IPasswordService> handler = new ChannelProxyHandler<IPasswordService>("NamedService"))
                {
                    IPasswordService channel = (IPasswordService)handler.GetTransparentProxy();

                    channel.GetThreadPassword();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler handles closing the channel when service is no longer available.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerHandlesClosingTheChannelWhenServiceIsNoLongerAvailableTest()
        {
            const String ServiceAddress = "net.pipe://localhost/ChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(TestService)))
            {
                host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<ITestService> handler = new ChannelProxyHandler<ITestService>())
                {
                    ITestService channel = (ITestService)handler.GetTransparentProxy();

                    channel.DoSomething();

                    host.Close();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler initializes factory after factory is already created.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerInitializesFactoryAfterFactoryIsAlreadyCreatedTest()
        {
            const String ServiceAddress = "net.pipe://localhost/NamedChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(PasswordService)))
            {
                host.AddServiceEndpoint(typeof(IPasswordService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<IPasswordService> handler = new ChannelProxyHandler<IPasswordService>("NamedService"))
                {
                    IPasswordService channel = (IPasswordService)handler.GetTransparentProxy();

                    channel.GetThreadPassword();

                    handler.Initialize<ChannelFactory<IPasswordService>>(factory => factory.Credentials.UserName.UserName = "Test");

                    channel.GetThreadPassword();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler initializes factory before factory is already created.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerInitializesFactoryBeforeFactoryIsAlreadyCreatedTest()
        {
            const String ServiceAddress = "net.pipe://localhost/NamedChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(PasswordService)))
            {
                host.AddServiceEndpoint(typeof(IPasswordService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<IPasswordService> handler = new ChannelProxyHandler<IPasswordService>("NamedService"))
                {
                    IPasswordService channel = (IPasswordService)handler.GetTransparentProxy();

                    handler.Initialize<ChannelFactory<IPasswordService>>(factory => factory.Credentials.UserName.UserName = "Test");

                    channel.GetThreadPassword();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler resolves endpoint from type.
        /// </summary>
        [TestMethod]
        public void ChannelProxyHandlerResolvesEndpointFromTypeTest()
        {
            const String ServiceAddress = "net.pipe://localhost/ChannelProxyHandlerTests";

            using (ServiceHost host = new ServiceHost(typeof(TestService)))
            {
                host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), ServiceAddress);
                host.Open();

                using (ChannelProxyHandler<ITestService> handler = new ChannelProxyHandler<ITestService>())
                {
                    ITestService channel = (ITestService)handler.GetTransparentProxy();

                    channel.DoSomething();
                }
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler throws exception for non configured service.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ConfigurationErrorsException))]
        public void ChannelProxyHandlerThrowsExceptionForNonConfiguredServiceTest()
        {
            using (new ChannelProxyHandler<IUnknownService>())
            {
            }
        }

        /// <summary>
        /// Runs test for channel proxy handler throws exception with multiple endpoint names found and no name specified.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ConfigurationErrorsException))]
        public void ChannelProxyHandlerThrowsExceptionWithMultipleEndpointNamesFoundAndNoNameSpecifiedTest()
        {
            using (new ChannelProxyHandler<IPasswordService>())
            {
            }
        }

        /// <summary>
        /// Gets or sets the test context which provides
        ///   information about and functionality for the current test run.
        /// </summary>
        /// <value>
        /// The test context.
        /// </value>
        public TestContext TestContext
        {
            get;
            set;
        }
    }
}